#!/bin/env python
# -*- coding: utf-8 -*-

#This file is part of pyLot library.
#
# pyLot is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pyLot is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with pyLot.  If not, see <http://www.gnu.org/licenses/>.

__author__ = u'Pierre Puiseux, Guillaume Baty'
__copyright__ = u"Copyright 2011-2012 (C) andheo, Université de Pau et des Pays de l'Adour"
__credits__ = [u'Pierre Puiseux', u'Guillaume Baty']
__license__ = "GNU Lesser General Public License"

u"""
Interfaces thread/process
"""

__all__ = [u'CommonIOHandler']

from traits.api import implements

from _pylot.minimal.utils import good_level, check_type, error, check_implements
from _pylot.minimal.interfaces.i_logger import ILogger
from _pylot.minimal.interfaces.i_iohandler import IIOHandler

class CommonIOHandler (object):
  u"""
  Class used to write message to user.
  
  The purpose of this class is to easily change text outputs without writing
  code again. For example, if you write a script and display a text in 
  (using this class), you can use the same code in script, console and graphical
  user interface (GUI)

  Example

  In a python file, you write :
  display.write('Welcome', style='title')
  display.write('Hello world')

  The result will depend of the implementations

  * A console implementation can display ::

    Welcome
    =-=-=-=
    Hello world
  
  * A GUI implementation can display a big blue "Welcome" and a small 
  "Hello world" in a popup windows or in a text view ...
  
  There are THREE views :
  
    - textout (write method)
    - stdout (writeStdout method)
    - stderr (writeStderr method)
    
  Text is written to all writers defined in "view_writers" attributes
  If view_writers list is empty, text is lost.
  """

  implements(IIOHandler)
  _level = u'standard'

  def __init__ (self, stdout=None, stderr=None, status=None,
                display_level=u'standard'):
    self._level = display_level

    self._stdout_writers = []
    self._stderr_writers = []
    self._textout_writers = []
    self._status_writers = []

    self._stdout = stdout
    self._stderr = stderr

    self._textout = self._stdout
    self._status = status

  def _setlevel (self, display_level):
    self._level = display_level

  level = property(
    fget=lambda self: self._level,
    fset=_setlevel
    )

  def _setstdout_writers (self, writer_list):
    for writer in writer_list :
      check_implements(u'writer', writer, ILogger)
    self._stdout_writers = writer_list

  def _setstderr_writers (self, writer_list):
    for writer in writer_list :
      check_implements(u'writer', writer, ILogger)
    self._stderr_writers = writer_list

  def _settextout_writers (self, writer_list):
    for writer in writer_list :
      check_implements(u'writer', writer, ILogger)
    self._textout_writers = writer_list

  def _setstatus_writers (self, writer_list):
    for writer in writer_list :
      check_implements(u'writer', writer, ILogger)
    self._status_writers = writer_list

  stdout_writers = property(
    fget=lambda self: self._stdout_writers,
    fset=_setstdout_writers
    )

  stderr_writers = property(
    fget=lambda self: self._stderr_writers,
    fset=_setstderr_writers
    )

  textout_writers = property(
    fget=lambda self: self._textout_writers,
    fset=_settextout_writers
    )

  status_writers = property(
    fget=lambda self: self._status_writers,
    fset=_setstatus_writers
    )

  def _setstdout (self, writer):
    check_implements(u'writer', writer, ILogger)
    self._stdout = writer

  def _setstderr (self, writer):
    check_implements(u'writer', writer, ILogger)
    self._stderr = writer

  def _settextout (self, writer):
    check_implements(u'writer', writer, ILogger)
    self._textout = writer

  def _setstatus (self, writer):
    check_implements(u'writer', writer, ILogger)
    self._status = writer

  stdout = property(
    fget=lambda self: self._stdout,
    fset=_setstdout
    )

  stderr = property(
    fget=lambda self: self._stderr,
    fset=_setstderr
    )

  textout = property(
    fget=lambda self: self._textout,
    fset=_settextout
    )

  status = property(
    fget=lambda self: self._status,
    fset=_setstatus
    )

  def goodLevel (self, option_level):
    u"""
    Test display level using good_level function.

    Dictionnary used is :
      'silent':0, 'standard':1, 'verbose':5, 'debug':10
    Current level is defined by :
      ShellManager.io.level

    .. seealso ::

      :func:`~_pylot.minimal.good_level` function

    """
    level_dict = {u'silent':0, u'standard':1, u'verbose':5, u'debug':10}
    return good_level(option_level, level_dict, self.level)

  def write (self, text, style=u'std', level=u'standard', writers=None):
    u"""
    write 'text' to user if 'level' matches manager current display level 
    Text format depend of style value. 
    Text is written in "text view" (also called default view).
    
    Available styles :

      - **std** : normal text, default
      - **title** : header/section title
      - **warn** : warning information
      - **info** : additionnal information, comments style
      
    Available levels :

      - **silent** : text always displayed. use this level only for very very important messages
      - **standard** : standard program output
      - **verbose** : additionnal optional messages
      - **debug** : messages useful for debug purposes only

    :param text: text you want to write
    :type text: str
    :param style: text style 
    :type style: str
    :param level: 'display level' of this text
    :type level: str
    :param writers: list of writers. If None (default) uses default IOHandler writers
    :type writers: list
    """
    if self.goodLevel(level) :
      assert (isinstance(writers, list) or writers is None)
      if writers is None :
        writers = self.textout_writers
      for writer in writers :
        writer.write(text, style)

  def writeStatus (self, text, style=u'std', level=u'standard'):
    u"""
    Like :meth:`pylot.core.IIOHandler.write` but write in
    "status" view if available else in default view.
    A StatusBar is an example of status view
    """
    self.write(text, style, level, self._status_writers)

  def writePopUp (self, text, title=None, style=u'std', level=u'standard'):
    u"""
    Like :meth:`pylot.core.IIOHandler.write` but write in
    a popup view if available else in default view.
    """
    if self.goodLevel(level) :
      if title is None and style == u'warn':
        title = tr_(u'Warning')
      elif title is None :
        title = tr_(u'Information')
      self._writePopUp(text, title, style)

  def writeStdout (self, text, style=u'std', level=u'standard'):
    u"""
    Like :meth:`pylot.core.IIOHandler.write` but write in
    "stdout" view if available else in default view.
    """
    self.write(text, style, level, self.stdout_writers)

  def writeStderr (self, text, style=u'std', level=u'standard'):
    u"""
    Like :meth:`pylot.core.IIOHandler.write` but write in
    "stderr" view if available else in default view.
    """
    self.write(text, style, level, self.stderr_writers)

  def writeBoth (self, text, style=u'std', level=u'standard'):
    u"""
    Like :meth:`pylot.core.IIOHandler.write` but write in
    both "stdout" and "stderr" views if availables else in default view.
    
    .. warning ::
      writeBoth hasn't "writers" parameters ! 
    """
    self.writeStderr(text, style, level)
    self.writeStdout(text, style, level)

  def writeAll (self, text, style=u'std', level=u'standard'):
    self.writeBoth(text, style, level)
    self.writeStatus(text, style, level)

  def rawInput (self, text=u''):
    u"""
    Like standard python function :func:`raw_input`.
    """
    check_type(u'text', text, unicode)
    return self._rawInput (text)

  def yesNoQuestion(self, text=u''):
    check_type(u'text', text, unicode)
    rep = self._yesNoQuestion(text)
    check_type(u'rep', rep, bool)
    return rep

  def _writePopUp(self, text, title, style=u'std'):
    self.writeStdout(title, style=u'title')
    self.writeStdout(text, style)

  def _rawInput (self, text):
    u"""
    text is supposed to be unicode
    """
    raise NotImplementedError

  def _yesNoQuestion (self, text):
    u"""
    text is supposed to be unicode.
    Must return boolean.
    """
    raise NotImplementedError

  def editData (self, data_obj, text=u''):
    u"""
    Edit internal state of object data_obj.
    Type supported : Collection, Book, Datum

    For developers, you must implement methods _editXyz.
    Xyz is data_obj type. These method provide an editor for data_obj and
    modify data_obj when quit.
    Only a limited number of type are allowed to keep interface implementation 
    possible so only implement existing _editXyz methods.
    """
    nature = data_obj.__class__.__name__
    if nature not in (u'Collection', u'Book', u'Datum') :
      raise ValueError, error(tr_(u'%s type not supported by editData method') % nature)
    else :
      method = getattr(self, u'_edit%s' % nature)
      return method(data_obj, text)

  def readSettings(self, settings, common=None):
    if common is None :
      common = settings
    settings.beginGroup(u'IoHandler')
    self.level = settings.getValue(u"level", self.level).value
    settings.endGroup()

  def writeSettings(self, settings, common=None):
    if common is None :
      common = settings
    settings.beginGroup(u'IoHandler')
    settings.setValue(u"level", self.level)
    settings.endGroup()


  def _editCollection (self, collection, text):
    raise NotImplementedError

  def _editBook (self, book, text):
    raise NotImplementedError

  def _editDatum (self, datum, text):
    raise NotImplementedError

  def resetToDefaultWriters (self):
    self._resetToDefaultWriters()

  def _resetToDefaultWriters (self):
    self._stdout_writers = [self._stdout]
    self._stderr_writers = [self._stderr]
    self._textout_writers = [self._textout]
    self._status_writers = [self._status]
